/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Xceed.Silverlight.Utils;

namespace Xceed.Silverlight.Controls
{
  [TemplateVisualState( GroupName = "CommonStates", Name = "Disabled" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "Normal" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "MouseOver" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Unfocused" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Focused" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "Licensed" )]
  [TemplateVisualState( GroupName = "LicensingStates", Name = "NotLicensed" )]
  public class DataNavigationControl : ItemsControl
  {
    #region Constructors

    public DataNavigationControl()
    {

      // We do not want any container recycling for this control
      VirtualizingStackPanel.SetVirtualizationMode( this, VirtualizationMode.Standard );
      this.DefaultStyleKey = typeof( DataNavigationControl );

      // The DataNavigationControl itself should not be IsTabStop, the
      // Children in its VisualTree should be.
      this.IsTabStop = false;

      this.Loaded += this.OnLoaded;
      this.Unloaded += this.OnUnloaded;
      this.IsEnabledChanged += this.OnIsEnabledChanged;
    }

    #endregion

    #region DataNavigationConfiguration Attached Property

    public static readonly DependencyProperty DataNavigationConfigurationProperty = DependencyProperty.RegisterAttached(
      "DataNavigationConfiguration",
      typeof( DataNavigationConfiguration ),
      typeof( DataNavigationControl ),
      new PropertyMetadata( null ) );

    public static DataNavigationConfiguration GetDataNavigationConfiguration( DependencyObject obj )
    {
      return ( DataNavigationConfiguration )obj.GetValue( DataNavigationControl.DataNavigationConfigurationProperty );
    }

    public static void SetDataNavigationConfiguration( DependencyObject obj, DataNavigationConfiguration value )
    {
      obj.SetValue( DataNavigationControl.DataNavigationConfigurationProperty, value );
    }

    #endregion

    #region IsFocused Internal Property

    internal bool IsFocused
    {
      get
      {
        return m_isFocused;
      }
      set
      {
        if( m_isFocused != value )
        {
          m_isFocused = value;
          this.ChangeVisualState();
        }
      }
    }

    private bool m_isFocused;

    #endregion

    #region IsMouseOver Internal Property

    internal bool IsMouseOver
    {
      get
      {
        return m_isMouseOver;
      }
      set
      {
        if( m_isMouseOver != value )
        {
          m_isMouseOver = value;
          this.ChangeVisualState();
        }
      }
    }

    private bool m_isMouseOver;

    #endregion

    #region Orientation Property

    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
      "Orientation",
      typeof( Orientation ),
      typeof( DataNavigationControl ),
      new PropertyMetadata( Orientation.Vertical ) );

    public Orientation Orientation
    {
      get
      {
        return ( Orientation )this.GetValue( DataNavigationControl.OrientationProperty );
      }
      set
      {
        this.SetValue( DataNavigationControl.OrientationProperty, value );
      }
    }

    #endregion

    #region DataNavigationHost Property

    public static readonly DependencyProperty DataNavigationHostProperty = DependencyProperty.Register(
      "DataNavigationHost",
      typeof( IDataNavigationHost ),
      typeof( DataNavigationControl ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( DataNavigationControl.OnDataNavigationHostChanged ) ) );

    public IDataNavigationHost DataNavigationHost
    {
      get
      {
        return ( IDataNavigationHost )this.GetValue( DataNavigationControl.DataNavigationHostProperty );
      }
      set
      {
        this.SetValue( DataNavigationControl.DataNavigationHostProperty, value );
      }
    }

    private static void OnDataNavigationHostChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      DataNavigationControl dataNavigationControl = sender as DataNavigationControl;

      if( dataNavigationControl == null )
        return;

      IDataNavigationHost newValue = e.NewValue as IDataNavigationHost;

      if( newValue != null )
      {
        dataNavigationControl.Keys = (newValue.Configuration != null)
          ? newValue.Configuration.GetKeys()
          : null;

        dataNavigationControl.NavigateToKeyCommand = newValue.NavigateToKeyCommand;
      }
      else
      {
        dataNavigationControl.Keys = null;
        dataNavigationControl.NavigateToKeyCommand = null;
      }
    }

    #endregion

    #region NavigateToKeyCommand Property

    public static readonly DependencyProperty NavigateToKeyCommandProperty = DependencyProperty.Register(
      "NavigateToKeyCommand",
      typeof( ICommand ),
      typeof( DataNavigationControl ),
      new PropertyMetadata( null ) );

    public ICommand NavigateToKeyCommand
    {
      get
      {
        return ( ICommand )this.GetValue( DataNavigationControl.NavigateToKeyCommandProperty );
      }
      set
      {
        this.SetValue( DataNavigationControl.NavigateToKeyCommandProperty, value );
      }
    }

    #endregion

    #region Keys Property

    public static readonly DependencyProperty KeysProperty = DependencyProperty.Register(
      "Keys",
      typeof( IList ),
      typeof( DataNavigationControl ),
      new PropertyMetadata(
        ( IList )null,
        new PropertyChangedCallback( DataNavigationControl.OnGroupsChanged ) ) );

    public IList Keys
    {
      get
      {
        return ( IList )this.GetValue( DataNavigationControl.KeysProperty );
      }
      private set
      {
        this.SetValue( DataNavigationControl.KeysProperty, value );
      }
    }

    private static void OnGroupsChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      DataNavigationControl dataNavigationControl = sender as DataNavigationControl;

      if( dataNavigationControl == null )
        return;

      dataNavigationControl.ItemsSource = e.NewValue as IList;
    }

    #endregion


    #region Protected Methods

    protected void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    protected void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLicensingStates( useTransitions );
      this.ChangeVisualStateCore( useTransitions );
    }

    protected internal virtual void ChangeVisualStateCore( bool useTransitions )
    {
      this.UpdateCommonStates( useTransitions );
      this.UpdateFocusStates( useTransitions );
    }


    protected override DependencyObject GetContainerForItemOverride()
    {
      return new DataNavigationItem();
    }

    protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
    {
      base.PrepareContainerForItemOverride( element, item );

      DataNavigationItem container = element as DataNavigationItem;
      Binding navigateToGroupCommandBinding = new Binding();
      navigateToGroupCommandBinding.Source = this;
      navigateToGroupCommandBinding.Mode = BindingMode.OneWay;
      navigateToGroupCommandBinding.Path = new PropertyPath( "NavigateToKeyCommand" );

      Binding commandParameterBinding = new Binding();
      commandParameterBinding.Mode = BindingMode.OneWay;

      container.DataContext = item;
      container.SetBinding( DataNavigationItem.CommandProperty, navigateToGroupCommandBinding );
      container.SetBinding( DataNavigationItem.CommandParameterProperty, commandParameterBinding );

      DataNavigationConfiguration configuration = ( this.DataNavigationHost != null ) ? this.DataNavigationHost.Configuration : null;

      Style containerStyle = null;
      DataTemplate contentTemplate = null;

      if( configuration != null )
      {
        containerStyle = configuration.ItemContainerStyle;
        contentTemplate = configuration.ItemTemplate;
      }

      if( containerStyle == null )
      {
        container.ClearValue( DataNavigationItem.StyleProperty );
      }
      else
      {
        container.Style = containerStyle;
      }

      if( contentTemplate == null )
      {
        container.ClearValue( DataNavigationItem.ContentTemplateProperty );
      }
      else
      {
        container.ContentTemplate = contentTemplate;
      }
    }

    protected virtual void OnIsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      this.ChangeVisualState();
    }

    protected override void OnGotFocus( RoutedEventArgs e )
    {
      base.OnGotFocus( e );
      this.IsFocused = FocusHelper.HasFocus( this );
    }

    protected override void OnLostFocus( RoutedEventArgs e )
    {
      base.OnLostFocus( e );
      this.IsFocused = FocusHelper.HasFocus( this );
    }

    protected override void OnMouseEnter( MouseEventArgs e )
    {
      base.OnMouseEnter( e );
      this.IsMouseOver = true;
    }

    protected override void OnMouseLeave( MouseEventArgs e )
    {
      base.OnMouseLeave( e );
      this.IsMouseOver = false;
    }

    #endregion

    #region Private Methods

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualStateCore( false );
    }

    private void OnUnloaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualStateCore( false );
    }

    private void UpdateLicensingStates( bool useTransitions )
    {
      if( m_isLicensed )
      {
        VisualStateManager.GoToState( this, "Licensed", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "NotLicensed", useTransitions );
      }
    }

    private void UpdateCommonStates( bool useTransitions )
    {
      if( !base.IsEnabled )
      {
        VisualStateManager.GoToState( this, "Disabled", useTransitions );
      }
      else if( this.IsMouseOver )
      {
        VisualStateManager.GoToState( this, "MouseOver", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "Normal", useTransitions );
      }
    }

    private void UpdateFocusStates( bool useTransitions )
    {
      if( this.IsFocused )
      {
        VisualStateManager.GoToState( this, "Focused", useTransitions );
      }
      else
      {
        VisualStateManager.GoToState( this, "Unfocused", useTransitions );
      }
    }

    #endregion

    #region Private fields

    //licensing
    private readonly Guid m_designTimeGuid;
    private readonly bool m_isInDesigner;
    private bool m_isLicensed = true;

    #endregion
  }
}
